#pragma once
#ifndef _KH_CORE_OBJECTBASE_H
#define _KH_CORE_OBJECTBASE_H
#include <Kharlia/Core/Common.h>
#include <Kharlia/Core/GC.h>

#define KH_DEF_GET(TYPE, NAME) inline TYPE Get##NAME() const { return m##NAME; }
#define KH_DEF_SET(TYPE, NAME) inline void Set##NAME(TYPE InValue) { m##NAME = InValue; }
#define KH_DEF_GETSET(TYPE, NAME) KH_DEF_GET(TYPE, NAME) KH_DEF_SET(TYPE, NAME)
#define KH_DEF_GET_P(TYPE, PRE, NAME) inline TYPE PRE##Get##NAME() const { return m##PRE##NAME; }
#define KH_DEF_SET_P(TYPE, PRE, NAME) inline void PRE##Set##NAME(TYPE InValue) { m##PRE##NAME = InValue; }
#define KH_DEF_GETSET_P(TYPE, PRE, NAME) KH_DEF_GET_P(TYPE, PRE, NAME) KH_DEF_SET_P(TYPE, PRE, NAME)

#define KH_HAS_BACK_REFERENCE(TYPE) \
    namespace boost { namespace python { template <> struct has_back_reference<TYPE>: mpl::true_ {};}}

namespace kharlia { namespace core {

void _InitObjectBindings();

/// This is the base for all native classes that are subclassed in Python
/// All instances must be created from Python and held by a shared_ptr
/// Some const versions of functions are included here for compatibility
class KH_CORE_DECLSPEC CObject: public boost::noncopyable, public enable_shared_from_this<CObject> {
    KH_DECL_GC()

protected:
    PyObject* mSelf;
    
public:
    CObject(PyObject* self);
    virtual ~CObject();

    object GetSelf() const;
    handle<> GetSelfHandle() const;
    object GetSelfAttr(object name) const;
    object GetSelfAttr(const char* name) const;
    void SetSelfAttr(object name, object value) const;
    void SetSelfAttr(const char* name, object value) const;

    /// Return a shared_ptr<T> of self where T is CObject or a subclass.
    /// The shared_ptr will be managing a Python object.
    template <class T>
    shared_ptr<T> GetSelfPtr() const;

    /// Return a weak_ptr<T> of self where T is CObject or a subclass.
    /// The weak_ptr is created from the original shared_ptr held by the
    /// Boost.Python.instance object, retrieved with shared_from_this().
    ///
    /// WARNING: Do not convert the shared_ptr created from weak_ptr.lock()
    ///          To a Python object, instead use GetSelf().
    template <class T>
    weak_ptr<T> GetSelfWeakPtr() const;

    // equivalent to super(type, self)
    object GetSuper(object type) const;
    object GetSuper(py::type_info type) const;
    template <class T>
    object GetSuper() const;

    friend void _InitObjectBindings();
};

inline object CObject::GetSelf() const {
    return object(handle<>(borrowed(mSelf)));
}

inline handle<> CObject::GetSelfHandle() const {
    return handle<>(borrowed(mSelf));
}

template <class T>
inline shared_ptr<T> CObject::GetSelfPtr() const {
    T* ptr = dynamic_cast<T*>(const_cast<CObject*>(this));
    if (!ptr)
        return shared_ptr<T>();

    // Copied from Boost.Python's shared_ptr_from_python::construct()
    shared_ptr<void> hold_convertible_ref_count(
        (void*)0,
        converter::shared_ptr_deleter(handle<>(borrowed(mSelf)))
    );
    return shared_ptr<T>(hold_convertible_ref_count, ptr);
}

template <class T>
inline weak_ptr<T> CObject::GetSelfWeakPtr() const {
    return boost::static_pointer_cast<T>(boost::const_pointer_cast<CObject>(shared_from_this()));
}

template <class T>
inline object CObject::GetSuper() const {
    return GetSuper(type_id<T>());
}

template <class T, class BT>
inline weak_ptr<T> _GetWeakPtrImpl(enable_shared_from_this<BT>* p) {
    return weak_ptr<T>(boost::static_pointer_cast<T>(p->shared_from_this()));
}

template <class T>
inline weak_ptr<T> _GetWeakPtrImpl(void* p) {
    return weak_ptr<T>();
}

/// Safe weak_ptr creation. It's unsafe to create a weak_ptr from a 
/// shared_ptr created by Boost.Python. This function will create the
/// weak_ptr from the weak_ptr stored in the object if it subclasses
/// enable_shared_from_this.
///
/// returns weak_ptr<T>(p) if p is a normal shared_ptr or p is managing a
///     Python object when T is a subclass of enable_shared_from_this.
/// returns weak_ptr<T>() if p manages a Python object and T is not a
///     subclass of enable_shared_from_this.
template <class T>
inline weak_ptr<T> GetWeakPtr(shared_ptr<T> p) {
    if (boost::get_deleter<converter::shared_ptr_deleter>(p)) {
        return _GetWeakPtrImpl<T>(p.get());
    }
    return weak_ptr<T>(p);
}

// Implements reference counting with intrusive_ptr for smaller objects
class KH_CORE_DECLSPEC FRefCounted {
    volatile long mRefCount;

public:
    FRefCounted(long InRefCount=0): mRefCount(InRefCount) {}
    virtual ~FRefCounted() {}

    friend void intrusive_ptr_add_ref(FRefCounted*);
    friend void intrusive_ptr_release(FRefCounted*);
};

inline void intrusive_ptr_add_ref(FRefCounted* r) {
    BOOST_INTERLOCKED_INCREMENT(&r->mRefCount);
}

inline void intrusive_ptr_release(FRefCounted* r) {
    if (BOOST_INTERLOCKED_DECREMENT(&r->mRefCount) == 0)
        delete r;
}

}} // namespace kharlia::core

KH_HAS_BACK_REFERENCE(kharlia::core::CObject)

#endif